/**
 * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "compiler/optimizer/optimizations/inline_intrinsics.h"
#include "optimizer/analysis/loop_analyzer.h"
#include "compiler/optimizer/ir/analysis.h"
#include "runtime/include/coretypes/tagged_value.h"
#include "irtoc_ir_inline.h"
#include "plugins/ecmascript/runtime/runtime_call_id.h"

namespace ark::compiler {
template <typename T>
bool InlineLdConstant(IntrinsicInst *intrinsic, AnyBaseType anyType, T value)
{
    auto currentBlock = intrinsic->GetBasicBlock();
    auto graph = currentBlock->GetGraph();
    auto cnst = graph->FindOrCreateConstant(value);

    auto castToAnyInst = graph->CreateInstCastValueToAnyType(intrinsic->GetPc(), anyType, cnst);
    currentBlock->InsertAfter(castToAnyInst, intrinsic);

    intrinsic->ReplaceUsers(castToAnyInst);
    currentBlock->RemoveInst(intrinsic);
    return true;
}

bool InlineLdConstant(IntrinsicInst *intrinsic, DataType::Any value)
{
    auto currentBlock = intrinsic->GetBasicBlock();
    auto graph = currentBlock->GetGraph();
    auto cnst = graph->FindOrCreateConstant(value);

    intrinsic->ReplaceUsers(cnst);

    currentBlock->RemoveInst(intrinsic);
    return true;
}

using TaggedValue = ark::coretypes::TaggedValue;

bool InlineIntrinsics::InlineLdTrue(IntrinsicInst *intrinsic)
{
    return InlineLdConstant(intrinsic, AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE, 1U);
}

bool InlineIntrinsics::InlineLdFalse(IntrinsicInst *intrinsic)
{
    return InlineLdConstant(intrinsic, AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE, 0U);
}

bool InlineIntrinsics::InlineLdHole(IntrinsicInst *intrinsic)
{
    return InlineLdConstant(intrinsic, DataType::Any(TaggedValue::VALUE_HOLE));
}

bool InlineIntrinsics::InlineLdNull(IntrinsicInst *intrinsic)
{
    return InlineLdConstant(intrinsic, DataType::Any(TaggedValue::VALUE_NULL));
}

bool InlineIntrinsics::InlineLdUndefined(IntrinsicInst *intrinsic)
{
    return InlineLdConstant(intrinsic, DataType::Any(TaggedValue::VALUE_UNDEFINED));
}

bool InlineIntrinsics::InlineLdInfinity(IntrinsicInst *intrinsic)
{
    return InlineLdConstant(intrinsic, AnyBaseType::ECMASCRIPT_DOUBLE_TYPE, TaggedValue::VALUE_INFINITY);
}

bool InlineIntrinsics::InlineLdNan(IntrinsicInst *intrinsic)
{
    return InlineLdConstant(intrinsic, AnyBaseType::ECMASCRIPT_DOUBLE_TYPE, TaggedValue::VALUE_NAN);
}

bool InlineIntrinsics::InlineTypeOf(IntrinsicInst *intrinsic)
{
    auto anyType = intrinsic->GetInput(0).GetInst();
    if (anyType->GetOpcode() != Opcode::AnyTypeCheck) {
        return false;
    }
    auto type = anyType->CastToAnyTypeCheck()->GetAnyType();

    if (type == AnyBaseType::UNDEFINED_TYPE) {
        return false;
    }
    auto anyName = GetGraph()->CreateInstGetAnyTypeName(intrinsic->GetPc(), type);
    intrinsic->InsertAfter(anyName);
    intrinsic->ReplaceUsers(anyName);
    return true;
}

void InlineIntrinsics::CreateCompareClass(uint32_t pc, Inst *getClsInst, RuntimeInterface::ClassPtr receiver,
                                          BasicBlock *loadBb)
{
    auto loadClsInst = GetGraph()->CreateInstLoadImmediate(DataType::REFERENCE, pc, receiver);
    auto cmpInst = GetGraph()->CreateInstCompare(DataType::BOOL, pc, loadClsInst, getClsInst, DataType::REFERENCE,
                                                 ConditionCode::CC_EQ);
    auto ifInst = GetGraph()->CreateInstIfImm(DataType::BOOL, pc, cmpInst, 0, DataType::BOOL, ConditionCode::CC_NE);
    // We insert LoadImmediate in Dominate blockm because VN can be applied
    getClsInst->InsertAfter(loadClsInst);
    loadBb->AppendInst(cmpInst);
    loadBb->AppendInst(ifInst);
}

Inst *InlineIntrinsics::CreateCompareClassWithDeopt(uint32_t pc, Inst *getClsInst, RuntimeInterface::ClassPtr receiver,
                                                    Inst *saveState)
{
    auto loadClsInst = GetGraph()->CreateInstLoadImmediate(DataType::REFERENCE, pc, receiver);
    auto cmpInst = GetGraph()->CreateInstCompare(DataType::BOOL, pc, getClsInst, loadClsInst, DataType::REFERENCE,
                                                 ConditionCode::CC_NE);
    auto deoptInst = GetGraph()->CreateInstDeoptimizeIf(pc, cmpInst, saveState, DeoptimizeType::INLINE_IC);
    getClsInst->InsertAfter(deoptInst);
    getClsInst->InsertAfter(cmpInst);
    getClsInst->InsertAfter(loadClsInst);
    return deoptInst;
}

Inst *InsertLoadObject(Inst *input, uint32_t typeId, RuntimeInterface::MethodPtr method, uint32_t pc,
                       RuntimeInterface::FieldPtr field, ObjectType objType, Inst *insertAfter, DataType::Type dataType)
{
    auto loadObject = input->GetBasicBlock()->GetGraph()->CreateInstLoadObject(dataType, pc, input,
                                                                               TypeIdMixin {typeId, method}, field);
    loadObject->SetObjectType(objType);
    insertAfter->InsertAfter(loadObject);
    return loadObject;
}

void InsertCheckJSArrayLength(Inst *obj, Inst *index, Inst *insertAfter, RuntimeInterface::MethodPtr method,
                              uint32_t pc)
{
    auto currBb = insertAfter->GetBasicBlock();
    auto graph = currBb->GetGraph();
    // Insert Load Object length field for JsArray object
    auto length = InsertLoadObject(obj, TypeIdMixin::MEM_DYN_ARRAY_LENGTH_ID, method, pc, nullptr,
                                   ObjectType::MEM_DYN_ARRAY_LENGTH, insertAfter, DataType::INT32);

    auto cmpInst = graph->CreateInstCompare(DataType::BOOL, pc, index, length, DataType::INT32, ConditionCode::CC_LT);
    auto ifInst = graph->CreateInstIfImm(DataType::BOOL, pc, cmpInst, 0, DataType::BOOL, ConditionCode::CC_NE);
    length->InsertAfter(cmpInst);
    cmpInst->InsertAfter(ifInst);
    // Split block by call instruction
    auto currContBb = currBb->SplitBlockAfterInstruction(ifInst, true);
    auto storeBb = graph->CreateEmptyBlock(currBb);
    currBb->GetLoop()->AppendBlock(storeBb);
    currBb->AddSucc(storeBb);
    storeBb->AddSucc(currContBb);
    // calculate new length
    auto add = graph->CreateInstAdd(DataType::INT32, pc, index, graph->FindOrCreateConstant(1));
    // Store new length
    auto storeLength = graph->CreateInstStoreObject(
        DataType::INT32, pc, obj, add, TypeIdMixin {TypeIdMixin::MEM_DYN_ARRAY_LENGTH_ID, method}, nullptr);
    storeLength->SetObjectType(ObjectType::MEM_DYN_ARRAY_LENGTH);
    storeBb->AppendInst(add);
    add->InsertAfter(storeLength);
}

template <bool IS_LOAD>
Inst *InsertMemFromFieldInlined(IntrinsicInst *intrinsic, Inst *objInst, Inst *insertAfter, uint32_t offset)
{
    auto graph = objInst->GetBasicBlock()->GetGraph();
    auto saveState = intrinsic->GetSaveState();
    auto pc = objInst->GetPc();
    if constexpr (IS_LOAD) {
        auto loadObject = InsertLoadObject(objInst, offset, saveState->GetMethod(), pc, nullptr,
                                           ObjectType::MEM_DYN_INLINED, insertAfter, DataType::ANY);
        return loadObject;
    } else {
        auto storeObject =
            graph->CreateInstStoreObject(DataType::ANY, pc, objInst, intrinsic->GetInput(2).GetInst(),
                                         TypeIdMixin {offset, saveState->GetMethod()}, nullptr, false, true);
        storeObject->SetObjectType(ObjectType::MEM_DYN_INLINED);
        insertAfter->InsertAfter(storeObject);

        return storeObject;
    }
}

Inst *InsertBoundsCheck(Inst *array, Inst *index, Inst *saveState, uint32_t pc, Inst *insertAfter)
{
    auto graph = array->GetBasicBlock()->GetGraph();
    auto arrayLength = graph->CreateInstLenArray(DataType::INT32, pc, array);

    // Create BoundCheck instruction
    auto boundsCheck = graph->CreateInstBoundsCheck(DataType::INT32, pc, arrayLength, index, saveState);
    boundsCheck->SetFlag(inst_flags::CAN_DEOPTIMIZE);
    insertAfter->InsertAfter(boundsCheck);
    insertAfter->InsertAfter(arrayLength);

    return boundsCheck;
}

template <bool IS_LOAD, bool INSERT_BOUNDS_CHECK>
Inst *InsertMemFromField(IntrinsicInst *intrinsic, Inst *objInst, Inst *insertAfter, uint32_t offset)
{
    auto graph = objInst->GetBasicBlock()->GetGraph();
    auto saveState = intrinsic->GetSaveState();
    auto pc = objInst->GetPc();
    auto loadObject = InsertLoadObject(objInst, TypeIdMixin::MEM_DYN_PROPS_ID, saveState->GetMethod(), pc, nullptr,
                                       ObjectType::MEM_DYN_PROPS, insertAfter, DataType::REFERENCE);
    insertAfter = loadObject;
    Inst *index = graph->FindOrCreateConstant(offset);
    if constexpr (INSERT_BOUNDS_CHECK) {
        auto boundsCheck = InsertBoundsCheck(loadObject, index, saveState, pc, insertAfter);
        insertAfter = boundsCheck;

        index = boundsCheck;
    }

    Inst *array = nullptr;
    if constexpr (IS_LOAD) {
        auto loadArray = graph->CreateInstLoadArray(DataType::ANY, pc, loadObject, index);
        array = loadArray;
    } else {
        auto storeArray =
            graph->CreateInstStoreArray(DataType::ANY, pc, loadObject, index, intrinsic->GetInput(2).GetInst(), true);
        array = storeArray;
    }
    insertAfter->InsertAfter(array);

    return array;
}

Inst *InsertChangeClassInst(IntrinsicInst *intrinsic, Inst *objInst, Inst *insertAfter, uintptr_t klassAddr)
{
    auto graph = objInst->GetBasicBlock()->GetGraph();
    auto pc = objInst->GetPc();
    auto klass = reinterpret_cast<RuntimeInterface::ClassPtr>(klassAddr);
    auto loadClsInst = graph->CreateInstLoadImmediate(DataType::REFERENCE, pc, klass);
    auto storeClass = graph->CreateInstStoreObject(
        DataType::REFERENCE, pc, objInst, loadClsInst,
        TypeIdMixin {TypeIdMixin::MEM_DYN_CLASS_ID, intrinsic->GetSaveState()->GetMethod()}, nullptr, false, true);
    storeClass->SetObjectType(ObjectType::MEM_DYN_CLASS);
    insertAfter->InsertAfter(storeClass);
    insertAfter->InsertAfter(loadClsInst);
    return storeClass;
}

Inst *InsertPrototypeCheckInst(IntrinsicInst *intrinsic, uintptr_t protoMem, Inst **insertAfter)
{
    auto graph = intrinsic->GetBasicBlock()->GetGraph();
    auto saveState = intrinsic->GetSaveState();
    auto pc = intrinsic->GetPc();
    // Get Prototype handler
    auto protoHandler = graph->CreateInstLoadObjFromConst(DataType::REFERENCE, pc, protoMem);
    (*insertAfter)->InsertAfter(protoHandler);

    // Get Prototype object
    auto prototype = InsertLoadObject(protoHandler, TypeIdMixin::MEM_DYN_PROTO_HOLDER_ID, saveState->GetMethod(), pc,
                                      nullptr, ObjectType::MEM_DYN_PROTO_HOLDER, protoHandler, DataType::REFERENCE);

    // Get Prototype change marker
    auto prototypeMarker =
        InsertLoadObject(protoHandler, TypeIdMixin::MEM_DYN_PROTO_CELL_ID, saveState->GetMethod(), pc, nullptr,
                         ObjectType::MEM_DYN_PROTO_CELL, prototype, DataType::REFERENCE);

    // Get change field from the marker
    auto isChange = InsertLoadObject(prototypeMarker, TypeIdMixin::MEM_DYN_CHANGE_FIELD_ID, saveState->GetMethod(), pc,
                                     nullptr, ObjectType::MEM_DYN_CHANGE_FIELD, prototypeMarker, DataType::BOOL);

    auto deoptInst = graph->CreateInstDeoptimizeIf(pc, isChange, saveState, DeoptimizeType::INLINE_IC);

    isChange->InsertAfter(deoptInst);

    *insertAfter = deoptInst;
    return prototype;
}

template <bool IS_LOAD>
Inst *InsertAccessInst(IntrinsicInst *intrinsic, Inst *objInst, Inst *insertAfter,
                       RuntimeInterface::NamedAccessProfileData profile)
{
    uint32_t offset = profile.offset;
    RuntimeInterface::NamedAccessProfileType type = profile.type;
    switch (type) {
        case RuntimeInterface::NamedAccessProfileType::FIELD_INLINED:
            return InsertMemFromFieldInlined<IS_LOAD>(intrinsic, objInst, insertAfter, offset);
        case RuntimeInterface::NamedAccessProfileType::FIELD:
            return InsertMemFromField<IS_LOAD, false>(intrinsic, objInst, insertAfter, offset);
        case RuntimeInterface::NamedAccessProfileType::TRANSITION_INLINED: {
            ASSERT(!IS_LOAD);
            auto storeClass = InsertChangeClassInst(intrinsic, objInst, insertAfter, profile.cachedValue);
            return InsertMemFromFieldInlined<IS_LOAD>(intrinsic, objInst, storeClass, offset);
        }
        case RuntimeInterface::NamedAccessProfileType::TRANSITION: {
            ASSERT(!IS_LOAD);
            auto store = InsertMemFromField<IS_LOAD, true>(intrinsic, objInst, insertAfter, offset);
            InsertChangeClassInst(intrinsic, objInst, store->GetPrev(), profile.cachedValue);
            return store;
        }
        case RuntimeInterface::NamedAccessProfileType::PROTOTYPE_INLINED: {
            auto loadProto = InsertPrototypeCheckInst(intrinsic, profile.cachedValue, &insertAfter);
            return InsertMemFromFieldInlined<IS_LOAD>(intrinsic, loadProto, insertAfter, offset);
        }
        case RuntimeInterface::NamedAccessProfileType::PROTOTYPE: {
            auto loadProto = InsertPrototypeCheckInst(intrinsic, profile.cachedValue, &insertAfter);
            return InsertMemFromField<IS_LOAD, false>(intrinsic, loadProto, insertAfter, offset);
        }
        default:
            UNREACHABLE();
    }
}

void InsertDeoptimizeInst(uint32_t pc, SaveStateInst *ss, BasicBlock *loadBb)
{
    ASSERT(loadBb != nullptr);
    // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage)
    auto graph = loadBb->GetGraph();
    ASSERT(graph != nullptr);

    // If last class compare returns false we need to deoptimize the method.
    // So we construct instruction DeoptimizeIf and insert instead of IfImm inst.
    auto ifInst = loadBb->GetLastInst();
    ASSERT(ifInst != nullptr && ifInst->GetOpcode() == Opcode::IfImm);
    ASSERT(ifInst->CastToIfImm()->GetImm() == 0 && ifInst->CastToIfImm()->GetCc() == ConditionCode::CC_NE);

    auto compareInst = ifInst->GetInput(0).GetInst()->CastToCompare();
    ASSERT(compareInst != nullptr && compareInst->GetCc() == ConditionCode::CC_EQ);
    compareInst->SetCc(ConditionCode::CC_NE);

    auto deoptInst = graph->CreateInstDeoptimizeIf(pc, compareInst, ss, DeoptimizeType::INLINE_IC);

    loadBb->RemoveInst(ifInst);
    loadBb->AppendInst(deoptInst);
}

bool InlineIntrinsics::InlineLdObjByName(IntrinsicInst *intrinsic)
{
    return InlineObjByName<true>(intrinsic);
}

bool InlineIntrinsics::InlineStObjByName(IntrinsicInst *intrinsic)
{
    return InlineObjByName<false>(intrinsic);
}

bool InlineIntrinsics::InlineLdObjByValue(IntrinsicInst *intrinsic)
{
    return InlineObjByValue<true>(intrinsic);
}

bool InlineIntrinsics::InlineStObjByValue(IntrinsicInst *intrinsic)
{
    return InlineObjByValue<false>(intrinsic);
}

bool InlineIntrinsics::GetICForMemNamedAccess(IntrinsicInst *intrinsic)
{
    auto pc = intrinsic->GetPc();
    auto runtime = GetGraph()->GetRuntime();
    auto saveState = intrinsic->GetSaveState();
    ASSERT(saveState != nullptr);
    auto callerInst = saveState->GetCallerInst();
    if (callerInst != nullptr) {
        ASSERT(callerInst->IsInlined());
        auto func = callerInst->GetFunctionObject();
        if (func != 0) {
            return runtime->GetProfileDataForNamedAccess(GetGraph()->GetMethod(), func, pc, &namedAccessProfile_);
        }
        return runtime->GetProfileDataForNamedAccess(callerInst->GetCallMethod(), pc, &namedAccessProfile_);
    }
    return runtime->GetProfileDataForNamedAccess(GetGraph()->GetMethod(), pc, &namedAccessProfile_);
}

bool InlineIntrinsics::GetICForMemValueAccess(IntrinsicInst *intrinsic)
{
    auto pc = intrinsic->GetPc();
    auto runtime = GetGraph()->GetRuntime();
    auto saveState = intrinsic->GetSaveState();
    ASSERT(saveState != nullptr);
    auto callerInst = saveState->GetCallerInst();
    if (callerInst != nullptr) {
        ASSERT(callerInst->IsInlined());
        auto func = callerInst->GetFunctionObject();
        if (func != 0) {
            return runtime->GetProfileDataForValueAccess(GetGraph()->GetMethod(), func, pc, &namedAccessProfile_);
        }
        return runtime->GetProfileDataForValueAccess(callerInst->GetCallMethod(), pc, &namedAccessProfile_);
    }
    return runtime->GetProfileDataForValueAccess(GetGraph()->GetMethod(), pc, &namedAccessProfile_);
}

template <bool BY_NAME>
Inst *InlineIntrinsics::InsertCheckAndCastInstructions(IntrinsicInst *intrinsic)
{
    auto pc = intrinsic->GetPc();
    auto saveState = intrinsic->GetSaveState();
    auto objInst = intrinsic->GetInput(BY_NAME ? 1 : 0).GetInst();

    auto anyCheck = GetGraph()->CreateInstAnyTypeCheck(DataType::ANY, pc, objInst, saveState,
                                                       AnyBaseType::ECMASCRIPT_HEAP_OBJECT_TYPE);
    intrinsic->InsertBefore(anyCheck);

    auto castValue = GetGraph()->CreateInstCastAnyTypeValue(pc, anyCheck, AnyBaseType::ECMASCRIPT_HEAP_OBJECT_TYPE);
    // NOLINTNEXTLINE(readability-magic-numbers)
    castValue->SetFlag(inst_flags::NO_HOIST);
    anyCheck->InsertAfter(castValue);

    return castValue;
}

template <bool IS_LOAD>
void InlineIntrinsics::InlineObjByNameMonomorphic(IntrinsicInst *intrinsic, Inst *getClsInst, Inst *objInst)
{
    auto pc = intrinsic->GetPc();
    auto saveState = intrinsic->GetSaveState();
    auto prof = namedAccessProfile_[0];
    auto deopt = CreateCompareClassWithDeopt(pc, getClsInst, prof.klass, saveState);
    Inst *obj = objInst;
    if (prof.type == RuntimeInterface::NamedAccessProfileType::PROTOTYPE ||
        prof.type == RuntimeInterface::NamedAccessProfileType::PROTOTYPE_INLINED) {
        obj = getClsInst;
    }
    [[maybe_unused]] auto loadInst = InsertAccessInst<IS_LOAD>(intrinsic, obj, deopt, prof);

    if constexpr (IS_LOAD) {
        intrinsic->ReplaceUsers(loadInst);
    }

    intrinsic->GetBasicBlock()->RemoveInst(intrinsic);
}

template <bool IS_LOAD>
void InlineIntrinsics::InlineObjByNamePolymorphic(IntrinsicInst *intrinsic, Inst *getClsInst, Inst *objInst)
{
    auto pc = intrinsic->GetPc();
    auto saveState = intrinsic->GetSaveState();
    PhiInst *phiInst = nullptr;
    BasicBlock *loadBb = nullptr;
    BasicBlock *loadContBb = nullptr;
    Inst *loadInst = intrinsic;
    for (auto &prof : namedAccessProfile_) {
        if (loadBb == nullptr) {
            // Split block by call instruction
            loadBb = loadInst->GetBasicBlock();
            loadContBb = loadBb->SplitBlockAfterInstruction(loadInst, false);
            if constexpr (IS_LOAD) {
                phiInst = GetGraph()->CreateInstPhi(loadInst->GetType(), loadInst->GetPc());
                phiInst->ReserveInputs(namedAccessProfile_.size());
                loadContBb->AppendPhi(phiInst);
            }
        } else {
            auto newLoadBb = GetGraph()->CreateEmptyBlock(loadBb);
            loadBb->GetLoop()->AppendBlock(newLoadBb);
            loadBb->AddSucc(newLoadBb);
            loadBb = newLoadBb;
        }

        CreateCompareClass(pc, getClsInst, prof.klass, loadBb);
        // Create load_inserting_block
        auto loadInsertingBlock = GetGraph()->CreateEmptyBlock(loadBb);
        loadBb->GetLoop()->AppendBlock(loadInsertingBlock);
        loadBb->AddSucc(loadInsertingBlock);
        auto lastSs = CopySaveState(GetGraph(), saveState);
        loadInsertingBlock->PrependInst(lastSs);
        Inst *obj = objInst;
        if (prof.type == RuntimeInterface::NamedAccessProfileType::PROTOTYPE ||
            prof.type == RuntimeInterface::NamedAccessProfileType::PROTOTYPE_INLINED) {
            obj = getClsInst;
        }

        [[maybe_unused]] auto load = InsertAccessInst<IS_LOAD>(intrinsic, obj, lastSs, prof);

        if constexpr (IS_LOAD) {
            phiInst->AppendInput(load);
        }

        loadInsertingBlock->AddSucc(loadContBb);
    }

    InsertDeoptimizeInst(pc, saveState, loadBb);

    if constexpr (IS_LOAD) {
        intrinsic->ReplaceUsers(phiInst);
    }

    intrinsic->GetBasicBlock()->RemoveInst(intrinsic);
}

void InsertCheckKeyInstructions(IntrinsicInst *intrinsic, uintptr_t keyMem)
{
    auto graph = intrinsic->GetBasicBlock()->GetGraph();
    auto pc = intrinsic->GetPc();
    auto saveState = intrinsic->GetSaveState();
    auto keyIc = graph->CreateInstLoadObjFromConst(DataType::ANY, pc, keyMem);
    auto cmpInst = graph->CreateInstCompare(DataType::BOOL, pc, keyIc, intrinsic->GetInput(1).GetInst(), DataType::ANY,
                                            ConditionCode::CC_NE);
    auto deoptInst = graph->CreateInstDeoptimizeIf(pc, cmpInst, saveState, DeoptimizeType::INLINE_IC);
    intrinsic->InsertBefore(keyIc);
    intrinsic->InsertBefore(cmpInst);
    intrinsic->InsertBefore(deoptInst);
}

template <bool IS_LOAD>
void InlineIntrinsics::InlineObjByValueWithKey(IntrinsicInst *intrinsic)
{
    auto pc = intrinsic->GetPc();
    auto saveState = intrinsic->GetSaveState();
    auto prof = namedAccessProfile_[0];
    InsertCheckKeyInstructions(intrinsic, prof.key);
    auto castValue = InsertCheckAndCastInstructions<false>(intrinsic);
    auto getClsInst = InsertLoadObject(castValue, TypeIdMixin::MEM_DYN_CLASS_ID, intrinsic->GetSaveState()->GetMethod(),
                                       pc, nullptr, ObjectType::MEM_DYN_CLASS, castValue, DataType::REFERENCE);

    auto deopt = CreateCompareClassWithDeopt(pc, getClsInst, prof.klass, saveState);
    Inst *obj = castValue;
    if (prof.type == RuntimeInterface::NamedAccessProfileType::PROTOTYPE ||
        prof.type == RuntimeInterface::NamedAccessProfileType::PROTOTYPE_INLINED) {
        obj = getClsInst;
    }
    [[maybe_unused]] auto loadInst = InsertAccessInst<IS_LOAD>(intrinsic, obj, deopt, prof);

    if constexpr (IS_LOAD) {
        intrinsic->ReplaceUsers(loadInst);
    }

    intrinsic->GetBasicBlock()->RemoveInst(intrinsic);
}

template <bool IS_LOAD>
void InlineIntrinsics::InlineObjByValueFromElements(IntrinsicInst *intrinsic)
{
    auto pc = intrinsic->GetPc();
    auto saveState = intrinsic->GetSaveState();

    // Check that input is heap object
    auto castValue = InsertCheckAndCastInstructions<false>(intrinsic);

    // Insert Load class for the object
    auto getClsInst = InsertLoadObject(castValue, TypeIdMixin::MEM_DYN_CLASS_ID, saveState->GetMethod(), pc, nullptr,
                                       ObjectType::MEM_DYN_CLASS, castValue, DataType::REFERENCE);

    // check objects classes with first class from IC
    auto loadClsInst = GetGraph()->CreateInstLoadImmediate(DataType::REFERENCE, pc, namedAccessProfile_[0].klass);
    auto cmpInst = GetGraph()->CreateInstCompare(DataType::BOOL, pc, getClsInst, loadClsInst, DataType::REFERENCE,
                                                 ConditionCode::CC_NE);
    getClsInst->InsertAfter(loadClsInst);
    loadClsInst->InsertAfter(cmpInst);
    Inst *deoptInput = cmpInst;
    if (namedAccessProfile_.size() == 2) {
        // check objects classes with second class from IC
        auto loadClsInst1 = GetGraph()->CreateInstLoadImmediate(DataType::REFERENCE, pc, namedAccessProfile_[1].klass);
        auto cmpInst1 = GetGraph()->CreateInstCompare(DataType::BOOL, pc, getClsInst, loadClsInst1, DataType::REFERENCE,
                                                      ConditionCode::CC_NE);
        auto andInst = GetGraph()->CreateInstAnd(DataType::BOOL, pc, cmpInst, cmpInst1);
        deoptInput = andInst;
        cmpInst->InsertAfter(loadClsInst1);
        loadClsInst1->InsertAfter(cmpInst1);
        cmpInst1->InsertAfter(andInst);
    }
    // Insert deoptimize
    auto deoptInst = GetGraph()->CreateInstDeoptimizeIf(pc, deoptInput, saveState, DeoptimizeType::INLINE_IC);
    deoptInput->InsertAfter(deoptInst);

    auto key = intrinsic->GetInput(1).GetInst();
    auto anyCheck =
        GetGraph()->CreateInstAnyTypeCheck(DataType::ANY, pc, key, saveState, AnyBaseType::ECMASCRIPT_INT_TYPE);
    intrinsic->InsertBefore(anyCheck);

    auto castValueInt = GetGraph()->CreateInstCastAnyTypeValue(pc, anyCheck, AnyBaseType::ECMASCRIPT_INT_TYPE);
    // NOLINTNEXTLINE(readability-magic-numbers)
    castValueInt->SetFlag(inst_flags::NO_HOIST);
    anyCheck->InsertAfter(castValueInt);

    // Insert Load Object for Elements array
    auto loadObject = InsertLoadObject(castValue, TypeIdMixin::MEM_DYN_ELEMENTS_ID, saveState->GetMethod(), pc, nullptr,
                                       ObjectType::MEM_DYN_ELEMENTS, castValueInt, DataType::REFERENCE);

    // Insert LenArray and BoundChecks
    auto boundsCheck = InsertBoundsCheck(loadObject, castValueInt, saveState, pc, loadObject);

    if constexpr (IS_LOAD) {
        auto loadArray = GetGraph()->CreateInstLoadArray(DataType::ANY, pc, loadObject, boundsCheck);

        intrinsic->ReplaceUsers(loadArray);
        boundsCheck->InsertAfter(loadArray);
        auto cmpHoleInst = GetGraph()->CreateInstCompare(
            DataType::BOOL, pc, loadArray,
            GetGraph()->FindOrCreateConstant(DataType::Any(ark::coretypes::TaggedValue::VALUE_HOLE)), DataType::ANY,
            ConditionCode::CC_EQ);
        loadArray->InsertAfter(cmpHoleInst);

        auto deoptHoleInst = GetGraph()->CreateInstDeoptimizeIf(pc, cmpHoleInst, saveState, DeoptimizeType::HOLE);
        cmpHoleInst->InsertAfter(deoptHoleInst);
    } else {
        auto storeArray = GetGraph()->CreateInstStoreArray(DataType::ANY, pc, loadObject, boundsCheck,
                                                           intrinsic->GetInput(2).GetInst(), true);

        boundsCheck->InsertAfter(storeArray);
        if (namedAccessProfile_[0].type == RuntimeInterface::NamedAccessProfileType::ARRAY_ELEMENT) {
            InsertCheckJSArrayLength(castValue, castValueInt, boundsCheck, saveState->GetMethod(), pc);
        }
    }
    intrinsic->GetBasicBlock()->RemoveInst(intrinsic);
}

template <bool IS_LOAD>
bool InlineIntrinsics::InlineObjByValue(IntrinsicInst *intrinsic)
{
    if (GetGraph()->IsAotMode()) {
        return false;
    }
    ASSERT(intrinsic->GetSaveState() != nullptr);
    if (!GetICForMemValueAccess(intrinsic)) {
        return false;
    }
    ASSERT(!namedAccessProfile_.empty());
    if (namedAccessProfile_[0].key == 0) {
        // we need to fix length for ARRAY_ELEMENT
        if (namedAccessProfile_.size() == 2 && namedAccessProfile_[0].type != namedAccessProfile_[1].type) {
            return false;
        }
        InlineObjByValueFromElements<IS_LOAD>(intrinsic);
        return true;
    }

    InlineObjByValueWithKey<IS_LOAD>(intrinsic);
    return false;
}

template <bool IS_LOAD>
bool InlineIntrinsics::InlineObjByName(IntrinsicInst *intrinsic)
{
    if (GetGraph()->IsAotMode()) {
        return false;
    }
    auto pc = intrinsic->GetPc();
    ASSERT(intrinsic->GetSaveState() != nullptr);
    if (!GetICForMemNamedAccess(intrinsic)) {
        return false;
    }
    ASSERT(!namedAccessProfile_.empty());

    auto castValue = InsertCheckAndCastInstructions<true>(intrinsic);
    auto getClsInst = InsertLoadObject(castValue, TypeIdMixin::MEM_DYN_CLASS_ID, intrinsic->GetSaveState()->GetMethod(),
                                       pc, nullptr, ObjectType::MEM_DYN_CLASS, castValue, DataType::REFERENCE);

    if (namedAccessProfile_.size() == 1) {
        InlineObjByNameMonomorphic<IS_LOAD>(intrinsic, getClsInst, castValue);
        return true;
    }

    InlineObjByNamePolymorphic<IS_LOAD>(intrinsic, getClsInst, castValue);
    return true;
}

bool InlineIntrinsics::InlineResolveAllocResult(IntrinsicInst *intrinsic)
{
    auto ctorRes = intrinsic->GetInput(2U).GetInst();
    if (ctorRes->GetOpcode() != Opcode::CastValueToAnyType) {
        return false;
    }
    if (ctorRes->CastToCastValueToAnyType()->GetAnyType() != AnyBaseType::ECMASCRIPT_UNDEFINED_TYPE) {
        return false;
    }
    auto allocInst = intrinsic->GetInput(1U).GetInst();
    intrinsic->ReplaceUsers(allocInst);

    intrinsic->GetBasicBlock()->RemoveInst(intrinsic);
    return true;
}

profiling::AnyInputType InlineIntrinsics::GetAllowedTypeForInput(IntrinsicInst *intrinsic, size_t index)
{
    auto input = intrinsic->GetInput(index).GetInst();
    if (input->GetOpcode() == Opcode::AnyTypeCheck) {
        return input->CastToAnyTypeCheck()->GetAllowedInputType();
    }
    return profiling::AnyInputType::DEFAULT;
}

bool InlineIntrinsics::InlineStrictCompareDifferentTypes(IntrinsicInst *intrinsic, AnyBaseType type1, AnyBaseType type2)
{
    ASSERT(intrinsic->GetIntrinsicId() == RuntimeInterface::IntrinsicId::INTRINSIC_STRICT_EQ_DYN ||
           intrinsic->GetIntrinsicId() == RuntimeInterface::IntrinsicId::INTRINSIC_STRICT_NOT_EQ_DYN);
    if ((type1 == AnyBaseType::ECMASCRIPT_INT_TYPE || type1 == AnyBaseType::ECMASCRIPT_DOUBLE_TYPE) &&
        (type2 == AnyBaseType::ECMASCRIPT_INT_TYPE || type2 == AnyBaseType::ECMASCRIPT_DOUBLE_TYPE)) {
        return false;
    }

    auto input1 = intrinsic->GetInput(0).GetInst();
    auto input2 = intrinsic->GetInput(0).GetInst();
    auto allowedType1 = input1->GetOpcode() == Opcode::AnyTypeCheck
                            ? input1->CastToAnyTypeCheck()->GetAllowedInputType()
                            : profiling::AnyInputType::DEFAULT;
    auto allowedType2 = input2->GetOpcode() == Opcode::AnyTypeCheck
                            ? input1->CastToAnyTypeCheck()->GetAllowedInputType()
                            : profiling::AnyInputType::DEFAULT;

    if (IsAnyTypeCanBeSubtypeOf(SourceLanguage::ECMASCRIPT, type1, type2, allowedType1, allowedType2) == false) {
        auto result = intrinsic->GetIntrinsicId() == RuntimeInterface::IntrinsicId::INTRINSIC_STRICT_EQ_DYN ? 0U : 1U;
        return InlineLdConstant(intrinsic, AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE, result);
    }
    return false;
}

bool InlineIntrinsics::InlineStrictEqDyn(IntrinsicInst *intrinsic, AnyBaseType type1, AnyBaseType type2)
{
    return InlineStrictCompareDifferentTypes(intrinsic, type1, type2) ||
           IrtocInlineCompareStrictEqDyn(intrinsic, type1, type2);
}

bool InlineIntrinsics::InlineStrictNotEqDyn(IntrinsicInst *intrinsic, AnyBaseType type1, AnyBaseType type2)
{
    return InlineStrictCompareDifferentTypes(intrinsic, type1, type2) ||
           IrtocInlineCompareStrictNotEqDyn(intrinsic, type1, type2);
}

bool InlineIntrinsics::InlineCompareWithNull(IntrinsicInst *intrinsic, AnyBaseType type1, AnyBaseType type2)
{
    ASSERT(intrinsic->GetIntrinsicId() == RuntimeInterface::IntrinsicId::INTRINSIC_EQ_DYN ||
           intrinsic->GetIntrinsicId() == RuntimeInterface::IntrinsicId::INTRINSIC_NOT_EQ_DYN);
    if (type1 == AnyBaseType::UNDEFINED_TYPE || type2 == AnyBaseType::UNDEFINED_TYPE) {
        return false;
    }
    bool firstNull = (type1 == AnyBaseType::ECMASCRIPT_UNDEFINED_TYPE || type1 == AnyBaseType::ECMASCRIPT_NULL_TYPE);
    bool secondNull = (type2 == AnyBaseType::ECMASCRIPT_UNDEFINED_TYPE || type2 == AnyBaseType::ECMASCRIPT_NULL_TYPE);
    auto eqResult = intrinsic->GetIntrinsicId() == RuntimeInterface::IntrinsicId::INTRINSIC_EQ_DYN ? 1U : 0U;
    if (firstNull && secondNull) {
        return InlineLdConstant(intrinsic, AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE, eqResult);
    }
    Inst *otherInput;
    if (firstNull) {
        otherInput = intrinsic->GetInput(1).GetInst();
    } else if (secondNull) {
        otherInput = intrinsic->GetInput(0).GetInst();
    } else {
        return false;
    }
    if (otherInput->GetOpcode() == Opcode::AnyTypeCheck && otherInput->CastToAnyTypeCheck()->IsSpecialWasSeen()) {
        return false;
    }
    return InlineLdConstant(intrinsic, AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE, 1 - eqResult);
}

bool InlineIntrinsics::InlineEqDyn(IntrinsicInst *intrinsic, AnyBaseType type1, AnyBaseType type2)
{
    return InlineCompareWithNull(intrinsic, type1, type2) || IrtocInlineCompareEqDyn(intrinsic, type1, type2);
}

bool InlineIntrinsics::InlineNotEqDyn(IntrinsicInst *intrinsic, AnyBaseType type1, AnyBaseType type2)
{
    return InlineCompareWithNull(intrinsic, type1, type2) || IrtocInlineCompareNeDyn(intrinsic, type1, type2);
}

bool InlineIntrinsics::InlineToNumber(IntrinsicInst *intrinsic, AnyBaseType type1)
{
    auto specialAllowed = (GetAllowedTypeForInput(intrinsic, 0) & profiling::AnyInputType::SPECIAL) != 0;
    return ark::compiler::IrtocInlineToNumber(intrinsic, type1, specialAllowed);
}

bool InlineIntrinsics::InlineLdLexDyn(IntrinsicInst *intrinsic)
{
    auto *saveState = intrinsic->GetSaveState();
    ASSERT(saveState != nullptr);

    // level, slot
    ASSERT(intrinsic->GetImms().size() == 2U);
    // Name, LexEnv, SaveState
    ASSERT(intrinsic->GetInputsCount() == 3U);
    auto *loadLexVar = intrinsic;
    loadLexVar->SetIntrinsicId(RuntimeInterface::IntrinsicId::INTRINSIC_LD_LEX_VAR_DYN);
    ASSERT(loadLexVar->GetInput(2U).GetInst()->GetOpcode() == Opcode::SaveState);
    loadLexVar->RemoveInput(2U);
    loadLexVar->RemoveInput(0U);
    loadLexVar->ClearFlag(inst_flags::REQUIRE_STATE);
    InstAppender appender(loadLexVar->GetBasicBlock(), loadLexVar);

    auto pc = intrinsic->GetPc();
    auto *hole = GetGraph()->FindOrCreateConstant(DataType::Any(ark::coretypes::TaggedValue::VALUE_HOLE));
    auto *compare =
        GetGraph()->CreateInstCompare(DataType::BOOL, pc, loadLexVar, hole, DataType::ANY, ConditionCode::CC_EQ);
    appender.Append(compare);

    auto *deoptIf = GetGraph()->CreateInstDeoptimizeIf(pc, compare, saveState, DeoptimizeType::HOLE);
    appender.Append(deoptIf);

    InlineLdLexVarDyn(loadLexVar);
    return true;
}

Inst *InlineIntrinsics::GetParentLexEnv(InstAppender *appender, Inst *currentLexEnv, uint32_t level, uint32_t pc)
{
    auto *runtime = GetGraph()->GetRuntime();
    for (; level > 0U; --level) {
        auto *loadParent = GetGraph()->CreateInstLoadArray(
            DataType::ANY, pc, currentLexEnv, GetGraph()->FindOrCreateConstant(runtime->GetLexicalEnvParentEnvIndex()));
        appender->Append(loadParent);

        auto *parentRef = GetGraph()->CreateInstCastAnyTypeValue(pc, loadParent, AnyBaseType::ECMASCRIPT_ARRAY_TYPE);
        parentRef->SetFlag(inst_flags::NO_HOIST);
        appender->Append(parentRef);
        currentLexEnv = parentRef;
    }
    return currentLexEnv;
}

bool InlineIntrinsics::InlineStLexDyn(IntrinsicInst *intrinsic)
{
    ASSERT(intrinsic->GetImms().size() == 2U);
    // Name, Acc, LexEnv, SaveState
    ASSERT(intrinsic->GetInputsCount() == 4U);

    auto level = intrinsic->GetImms()[0U];
    if (level > GetLdStLexVarDynLevelThreshold()) {
        return false;
    }

    auto slot = intrinsic->GetImms()[1U];

    auto *storeAccVal = intrinsic->GetInput(1U).GetInst();
    auto *storeLexEnv = intrinsic->GetInput(2U).GetInst();

    InstAppender appender(intrinsic->GetBasicBlock(), intrinsic);

    auto pc = intrinsic->GetPc();
    auto *envRef = GetGraph()->CreateInstCastAnyTypeValue(pc, storeLexEnv, AnyBaseType::ECMASCRIPT_ARRAY_TYPE);
    envRef->SetFlag(inst_flags::NO_HOIST);
    appender.Append(envRef);

    Inst *currentLexEnv = envRef;
    currentLexEnv = GetParentLexEnv(&appender, currentLexEnv, level, pc);

    auto start = GetGraph()->GetRuntime()->GetLexicalEnvStartDataIndex();
    auto elemOffset = start + slot;

    auto saveState = intrinsic->GetSaveState();
    ASSERT(saveState != nullptr);

    auto *getProp =
        GetGraph()->CreateInstLoadArray(DataType::ANY, pc, currentLexEnv, GetGraph()->FindOrCreateConstant(elemOffset));
    appender.Append(getProp);

    auto *hole = GetGraph()->FindOrCreateConstant(DataType::Any(ark::coretypes::TaggedValue::VALUE_HOLE));
    auto *compare =
        GetGraph()->CreateInstCompare(DataType::BOOL, pc, getProp, hole, DataType::ANY, ConditionCode::CC_EQ);
    appender.Append(compare);

    auto *deoptIf = GetGraph()->CreateInstDeoptimizeIf(pc, compare, saveState, DeoptimizeType::HOLE);
    appender.Append(deoptIf);

    auto *storeLexVar = GetGraph()->CreateInstStoreArray(
        DataType::ANY, pc, currentLexEnv, GetGraph()->FindOrCreateConstant(elemOffset), storeAccVal, true);
    appender.Append(storeLexVar);

    if (intrinsic->HasUsers()) {
        intrinsic->ReplaceUsers(storeAccVal);
    }
    intrinsic->GetBasicBlock()->RemoveInst(intrinsic);
    return true;
}

bool InlineIntrinsics::InlineLdLexVarDyn(IntrinsicInst *intrinsic)
{
    auto level = intrinsic->GetImms()[0U];
    if (level > GetLdStLexVarDynLevelThreshold()) {
        return false;
    }

    auto slot = intrinsic->GetImms()[1U];

    auto *loadLexEnv = intrinsic->GetInput(0U).GetInst();

    InstAppender appender(intrinsic->GetBasicBlock(), intrinsic);

    auto pc = intrinsic->GetPc();
    auto *envRef = GetGraph()->CreateInstCastAnyTypeValue(pc, loadLexEnv, AnyBaseType::ECMASCRIPT_ARRAY_TYPE);
    envRef->SetFlag(inst_flags::NO_HOIST);
    appender.Append(envRef);

    Inst *currentLexEnv = envRef;
    currentLexEnv = GetParentLexEnv(&appender, currentLexEnv, level, pc);

    auto start = GetGraph()->GetRuntime()->GetLexicalEnvStartDataIndex();
    auto elemOffset = start + slot;
    auto *loadLexVar =
        GetGraph()->CreateInstLoadArray(DataType::ANY, pc, currentLexEnv, GetGraph()->FindOrCreateConstant(elemOffset));
    appender.Append(loadLexVar);

    intrinsic->ReplaceUsers(loadLexVar);
    intrinsic->GetBasicBlock()->RemoveInst(intrinsic);
    return true;
}

bool InlineIntrinsics::InlineLdlexenvDyn([[maybe_unused]] IntrinsicInst *intrinsic)
{
    auto pc = intrinsic->GetPc();
    auto loadLexicalEnv = GetGraph()->CreateInstLoadLexicalEnv(DataType::ANY, pc, intrinsic->GetInput(0).GetInst());
    intrinsic->InsertBefore(loadLexicalEnv);
    intrinsic->ReplaceUsers(loadLexicalEnv);
    intrinsic->GetBasicBlock()->RemoveInst(intrinsic);
    return true;
}

bool InlineIntrinsics::InlineStLexVarDyn(IntrinsicInst *intrinsic)
{
    auto level = intrinsic->GetImms()[0U];
    if (level > GetLdStLexVarDynLevelThreshold()) {
        return false;
    }

    auto slot = intrinsic->GetImms()[1U];

    auto *storeAccVal = intrinsic->GetInput(0U).GetInst();
    auto *storeLexEnv = intrinsic->GetInput(1U).GetInst();

    InstAppender appender(intrinsic->GetBasicBlock(), intrinsic);

    auto pc = intrinsic->GetPc();
    auto *envRef = GetGraph()->CreateInstCastAnyTypeValue(pc, storeLexEnv, AnyBaseType::ECMASCRIPT_ARRAY_TYPE);
    envRef->SetFlag(inst_flags::NO_HOIST);
    appender.Append(envRef);

    Inst *currentLexEnv = envRef;
    currentLexEnv = GetParentLexEnv(&appender, currentLexEnv, level, pc);

    auto start = GetGraph()->GetRuntime()->GetLexicalEnvStartDataIndex();
    auto elemOffset = start + slot;

    auto *storeLexVar = GetGraph()->CreateInstStoreArray(
        DataType::ANY, pc, currentLexEnv, GetGraph()->FindOrCreateConstant(elemOffset), storeAccVal, true);
    appender.Append(storeLexVar);

    if (intrinsic->HasUsers()) {
        intrinsic->ReplaceUsers(storeAccVal);
    }
    intrinsic->GetBasicBlock()->RemoveInst(intrinsic);
    return true;
}

bool InlineIntrinsics::InlinePopLexenvDyn(IntrinsicInst *intrinsic)
{
    InstAppender appender(intrinsic->GetBasicBlock(), intrinsic);

    auto pc = intrinsic->GetPc();
    auto *envRef = GetGraph()->CreateInstCastAnyTypeValue(pc, intrinsic->GetInput(0).GetInst(),
                                                          AnyBaseType::ECMASCRIPT_ARRAY_TYPE);
    envRef->SetFlag(inst_flags::NO_HOIST);
    appender.Append(envRef);

    auto *loadParent = GetGraph()->CreateInstLoadArray(
        DataType::ANY, pc, envRef,
        GetGraph()->FindOrCreateConstant(GetGraph()->GetRuntime()->GetLexicalEnvParentEnvIndex()));
    appender.Append(loadParent);

    intrinsic->ReplaceUsers(loadParent);
    intrinsic->GetBasicBlock()->RemoveInst(intrinsic);
    return true;
}

bool InlineIntrinsics::InlineTryLdGlobalByName(IntrinsicInst *inst)
{
    if (GetGraph()->IsAotMode()) {
        return false;
    }
    ASSERT(inst->GetSaveState() != nullptr);
    auto runtime = GetGraph()->GetRuntime();
    auto loadFromCp = inst->GetInput(0).GetInst();
    auto typeId = loadFromCp->CastToLoadFromConstantPool()->GetTypeId();
    auto pc = inst->GetPc();
    auto saveState = inst->GetSaveState();
    ASSERT(saveState != nullptr);
    auto callerInst = saveState->GetCallerInst();
    RuntimeInterface::GlobalVarInfo inlineInfo;
    if (callerInst != nullptr) {
        ASSERT(callerInst->IsInlined());
        auto func = callerInst->GetFunctionObject();
        if (func != 0) {
            inlineInfo = runtime->GetGlobalVarInfo(GetGraph()->GetMethod(), func, typeId, pc);
        } else {
            inlineInfo = runtime->GetGlobalVarInfo(callerInst->GetCallMethod(), typeId, pc);
        }
    } else {
        inlineInfo = runtime->GetGlobalVarInfo(GetGraph()->GetMethod(), typeId, pc);
    }
    switch (inlineInfo.type) {
        case RuntimeInterface::GlobalVarInfo::Type::CONSTANT: {
            auto constant = GetGraph()->FindOrCreateConstant(DataType::Any(inlineInfo.value));
            inst->ReplaceUsers(constant);
            inst->GetBasicBlock()->RemoveInst(inst);
            return true;
        }
        case RuntimeInterface::GlobalVarInfo::Type::NON_CONFIGURABLE:
            InlineTryLdGlobalField<false>(inst, typeId, static_cast<uintptr_t>(inlineInfo.value));
            return true;
        case RuntimeInterface::GlobalVarInfo::Type::FIELD:
            InlineTryLdGlobalField<true>(inst, typeId, static_cast<uintptr_t>(inlineInfo.value));
            return true;
        default:
            return false;
    }
}

template <bool NEED_GUARD>
void InlineIntrinsics::InlineTryLdGlobalField(IntrinsicInst *inst, uint32_t typeId, uintptr_t address)
{
    auto pc = inst->GetPc();
    auto saveState = inst->GetSaveState();

    auto getAddress =
        GetGraph()->CreateInstGetGlobalVarAddress(DataType::REFERENCE, pc, inst->GetInput(2).GetInst(), saveState,
                                                  TypeIdMixin {typeId, GetGraph()->GetMethod()}, address);
    inst->InsertBefore(getAddress);

    auto loadObject = GetGraph()->CreateInstLoadObject(
        DataType::ANY, pc, getAddress, TypeIdMixin {TypeIdMixin::MEM_DYN_GLOBAL_ID, GetGraph()->GetMethod()}, nullptr);
    loadObject->SetObjectType(ObjectType::MEM_DYN_GLOBAL);
    inst->InsertBefore(loadObject);

    if constexpr (NEED_GUARD) {
        auto cmp = GetGraph()->CreateInstCompareAnyType(pc, loadObject, AnyBaseType::ECMASCRIPT_HOLE_TYPE);
        auto deopt = GetGraph()->CreateInstDeoptimizeIf(pc, cmp, saveState, DeoptimizeType::INLINE_IC);
        inst->InsertBefore(cmp);
        inst->InsertBefore(deopt);
    }

    inst->ReplaceUsers(loadObject);
    inst->GetBasicBlock()->RemoveInst(inst);
}

}  // namespace ark::compiler

#include "plugins/ecmascript/runtime/builtins/generated/builtins_inline_intrinsics_gen.inl"
